#!/bin/bash
export LANG=C

[ -n "$TONE_ROOT" ] || {
    TONE_ROOT=$(readlink -f "$0")
    TONE_ROOT=$(dirname "$TONE_ROOT")
    TONE_ROOT=$(dirname "$TONE_ROOT")
    export TONE_ROOT
}
export TONE_CACHE_DIR=$TONE_ROOT/cache
export TONE_BUILD_DIR=$TONE_ROOT/build
export TONE_RUN_DIR=$TONE_ROOT/run
export TONE_RESULT_DIR=$TONE_ROOT/result
export TONE_SUITE_DIR=$TONE_ROOT/tests
export TONE_CONF_DIR=$TONE_ROOT/conf
os_distro=$(cat /etc/os-release | grep ^ID= | awk -F '=' '{print $2}')
export TONE_OS_DISTRO=$os_distro

if [ -n "$testsuite" ]; then
    export TONE_BM_CACHE_DIR=$TONE_CACHE_DIR/$testsuite
    export TONE_BM_BUILD_DIR=$TONE_BUILD_DIR/$testsuite
    export TONE_BM_RUN_DIR=$TONE_RUN_DIR/$testsuite
    export TONE_BM_SUITE_DIR=$TONE_SUITE_DIR/$testsuite
    export TONE_BM_RESULT_DIR=$TONE_RESULT_DIR/$testsuite
fi

update_known_host()
{
    while (($#))
    do
        ssh-keygen -R "$1"
        ssh-keyscan -H "$1" >> ~/.ssh/known_hosts 2>/dev/null
        shift
    done
}

set_ssh_key()
{
    local key=$1
    if [ ! -e "$key" ]; then
        return
    fi

    chmod 0600 "$key"
    export GIT_SSH_COMMAND="ssh -i $key"

    cat <<-EOF > "$TONE_ROOT/bin/git_ssh_wrapper"
ssh -o StrictHostKeyChecking=no -i $key "\$@"
EOF
    chmod +x "$TONE_ROOT/bin/git_ssh_wrapper"
    export GIT_SSH=$TONE_ROOT/bin/git_ssh_wrapper
}

# Output:
#   TONE_USER: username for git used in http format path
#   TONE_PASS: password for git used in http format path
#   GIT_SSH_COMMAND: Set ssh key for git command used for ssh format path
security_setting()
{
    tonerc="$TONE_ROOT/etc/tonerc"
    cat <<EOF
Read Private Settings
    $tonerc
EOF
    if [ -f "$tonerc" ]; then
        # shellcheck disable=SC1090
        . $tonerc
        if [ -n "${gitlab_user}" ]; then
            export TONE_USER=$gitlab_user
            export TONE_PASS=$gitlab_passwd
        fi
        if [ -n "${gitlab_key}" ]; then
            set_ssh_key "${gitlab_key}"
        fi
        if [ -n "${gitlab_host}" ]; then
            update_known_host ${gitlab_host}
        fi
    fi
}

web_download()
{
    local url=$1
    shift

    if [ -n "$1" ] && [ "$1" == "${1#-}" ]; then
        fname="$1"
        shift
    else
        fname=$(basename "$url")
    fi
    if [ "X$url" == "X" ]; then
        :
    else
        if [ -e "$fname" ]; then
            rm "$fname"
        fi
        wget -t 3 -T 3 --no-clobber "$url" -O "$fname"
    fi
}

wget_download()
{
    urls=()
    while (($#))
    do
        case $1 in
            http*://*)
                ;&
            # git@server:/
            *@*:*)
                urls[${#urls[*]}]=$1;;
            *) break;;
        esac
        shift
    done
    for i in ${urls[*]}; do
        web_download "$i" "$@" && break
    done
}

git_clone_mirror()
{
    set_git_clone_mirror
    wrapper_git_clone "$@"
}

git_clone_bare()
{
    set_git_clone_bare
    wrapper_git_clone "$@"
}

git_clone()
{
    set_git_clone
    wrapper_git_clone "$@"
}

wrapper_git_clone()
{
    urls=()
    while (($#))
    do
        case $1 in
            http*://*)
                ;&
            # git@server:/
            *@*:*)
                urls[${#urls[*]}]=$1;;
            *) break;;
        esac
        shift
    done
    for i in ${urls[*]}
    do
        echo $i
        git_clone_exec "$i" "$@"  && break
    done
}

set_git_clone_mirror()
{
    tone_git_clone_command="timeout 60m git clone --mirror"
}

set_git_clone_bare()
{
    tone_git_clone_command="timeout 60m git clone --bare"
}

set_git_clone()
{
    tone_git_clone_command="timeout 60m git clone"
}

git_clone_exec()
{
    local url=$1
    local dir
    local branch=master
    shift

    [ -z "$tone_git_clone_command" ] && set_git_clone_mirror

    if [ -n "$1" ] && [ "$1" == "${1#-}" ]; then
        dir="$1"
        shift
    else
        dir=$(basename "$url" .git)
    fi

    if [ "X$url" == "X" ]; then
        return
    fi

    if [ "$1" = "--branch" ] && [ -n "$2" ]; then
        branch=$2
    fi

    local git="timeout 60m git"

    echo "$dir"
    if [ -d "$dir/.git" ] || [ -e "$dir/HEAD" ]; then
        (
            cd "$dir" || exit 1
            echo "${git} remote update origin"
            $git remote update origin 2>&1

            if [ -d "$dir/.git" ]; then
                echo "$git checkout -q origin/$branch"
                $git checkout -q "origin/$branch" 2>&1
            fi
        )
    else
        #rm -fr "$dir" 2>/dev/null
        if [[ $(readlink -f $dir) == $(readlink -f $(pwd)) ]]; then
            for i in $(ls -A1 $dir); do
                rm -rf "$i"
            done
            [ ! -e "$dir" ] && mkdir "$dir"
            cd "$dir"
        else
            rm -rf "$dir"
            mkdir "$dir"
            cd "$dir"
        fi
        echo "$tone_git_clone_command" -q "$@" "$url" "$dir"
        $tone_git_clone_command -q "$@" "$url" "$dir" 2>&1
    fi
}

check_and_create_path()
{
    if [ -d "$1" ]; then
        :
    else
        mkdir -p "$1"
    fi
}

tone_byte()
{
    local v=${1,,}
    local size=${v%[a-Z]*}
    local unit=${v##*[0-9]}

    [ "$size" == "$v" ] && echo "$size" && return

    case $unit in
        k)
            echo $((size << 10))
            ;;
        m)
            echo $((size << 20))
            ;;
        g)
            echo $((size << 30))
            ;;
    esac
}

decompress()
{
    local file=$1
    local target_dir=$2
    if [ -n "$STRIP_LEVEL" ]; then
        taropt="--strip-components=$STRIP_LEVEL"
    fi
    case $file in
        *.tar)    ;&
        *.tar.Z)  ;&
        *.tar.xz) tar xf "$file" -C "$target_dir" "$taropt";;
        *.tar.gz|*.tgz) tar zxf "$file" -C "$target_dir" "$taropt";;
		*.tar.bz2) tar jxf "$file" -C "$target_dir" "$taropt";;
        *)
            echo "copy file $file"
            cp -af "$1" "$2";;
    esac
}

# Prepare code from cach direcotry to build directory
# single WEB_URL: copy file and decompress it if necessary
# single GIT_URL: clone and create work space
# Mixed WEB_URL and GIT_URL
# or multi WEB_URL
# or multi GIT_URL:
#   Copy file to directory named with file name without extension
#   clone git into basename of git path
extract_src()
{
    local src=$TONE_BM_CACHE_DIR
    local des=$TONE_BM_BUILD_DIR
    local branch=master
    local wrked=0
    local processed_target=""

    if [ -n "$BRANCH" ]; then
        branch=$BRANCH
    fi
    if [ -n "$GIT_URL" ]; then
        echo "Clone code to build path"
        (
            cd "$des" || exit 1
            set_git_clone
            if [[ ${#GIT_URL[@]} == 1 && $WEB_URL == '' ]]; then
                # only have single git repo
                git_clone_exec "$src" "$des" --branch "$branch"
            else
                # multi git repos or have download files
                for i in $(seq 0 $((${#GIT_URL[@]} - 1))); do
                    for rep in ${GIT_URL[$i]}; do
                        rep_name=$(basename $rep .git)
                        # if success then skip the next try
                        git_clone_exec "$src/$rep_name" "$des/$rep_name" --branch "$branch" && break
                    done
                done
            fi
        )
        wrked=1
    fi

    if [ -n "$WEB_URL" ]; then
        echo "Copy downloaded files to build path"
        (
            cd "$des" || exit 1
            if [[ ${#WEB_URL[@]} == 1 && $GIT_URL == '' ]]; then
                for url in ${WEB_URL}; do
                    filename=$(basename $url)
                    echo "decompress single file: $filename"
                    decompress "$src/$filename" "$des" && break
                done
            else
                for i in $(seq 0 $((${#WEB_URL[@]} - 1))); do
                    for url in ${WEB_URL[$i]}; do
                        filename=$(basename $url)
                        target="$des/${filename%%.*}"
                        echo "decompress file: $filename"
                        mkdir -p $target
                        decompress "$src/$filename" $target && break
                    done
                done
            fi
        )
        wrked=1
    fi

    if [ $wrked -eq 0 ]; then
        for f in "$src"/*; do
            cp $f $des
        done
    fi
}

is_pkg_installed()
{
    if which yum > /dev/null; then
        yum list installed "$1" >& /dev/null && return 0
    elif which dpkg > /dev/null; then
        dpkg -l | grep "$1"  >& /dev/null && return 0
    else
      return 1
    fi
}

uninstall_pkg()
{
    local pkg_installed="$TONE_BM_RESULT_DIR/.pkg_installed"
    if [ -e "$pkg_installed" ]; then
        sort -u "$pkg_installed" | while read -r pkg
        do
            if which yum > /dev/null; then
                yum erase -y "$pkg" || echo "Error: $pkg fail to uninstall"
            elif which apt > /dev/null; then
                apt remove -y "$pkg" || echo "Error: $pkg fail to uninstall"
            else
                echo "Error: Unsupported Distribution"
            fi
        done
    fi
}

check_python_link()
{
    if [ ! -f /usr/bin/python ]; then
        if [ -f /usr/bin/python3 ]; then
            ln -s /usr/bin/python3 /usr/bin/python
        elif [ -f /usr/bin/python2 ]; then
            ln -s /usr/bin/python2 /usr/bin/python
        else
            echo "Can Not find python in /usr/bin"
            exit 1
        fi
    fi
}

install_pkg()
{
    local DEP_PKG_LIST=$1
    local pkg_installed="$TONE_BM_RESULT_DIR/.pkg_installed"
    if [ "X$DEP_PKG_LIST" == "X" ]; then
        return
    fi

    if [ -n "$USE_TEST_REPO" ]; then
        if [ -n "$yumopts" ]; then
            yumopts="$yumopts -b test"
        else
            yumopts="-b test"
        fi
    fi

    for pkg in "$@"; do
        if (is_pkg_installed $pkg); then
            echo "$pkg installed"
        else
            echo "$pkg not installed"
            if which yum > /dev/null; then
                yum install $yumopts -y "$pkg"
            elif which apt > /dev/null; then
                apt install $yumopts -y "$pkg"
            else
                echo "Unsupported Distribution"
            fi
            if [ $? -eq 0 ]; then
                echo "Success: $pkg install completed"
                echo "$pkg" >> "$pkg_installed"
            else
                if [ -n "$TONE_CAN_SKIP_PKG" ]; then
                    echo "Skip to install $pkg"
                else
                    echo "Fail to install $pkg"
                    exit 1
                fi
            fi
        fi
    done

    # fix python link when python is updated
    check_python_link
}

patch_src()
{
    local patch=$TONE_BM_SUITE_DIR/patches/$1
    [ -f $patch ] || return 0
    patch -p1 < $patch
}

wget_by_array()
{
    for i in $(seq 0 $((${#WEB_URL[@]} - 1))); do
        for pack in ${WEB_URL[$i]}; do
            echo Download $pack
            wget_download $pack && break
        done
    done
}

git_clone_mirror_by_array()
{
    if [[ ${#GIT_URL[@]} == 1 && $WEB_URL == '' ]]; then
        # only have single git repo
        git_clone_mirror $GIT_URL $TONE_BM_CACHE_DIR
    else
        # multi git repos or have download files
        for i in $(seq 0 $((${#GIT_URL[@]} - 1))); do
            for rep in ${GIT_URL[$i]}; do
                # if success then skip the next try
                git_clone_mirror $rep "$TONE_BM_CACHE_DIR/$(basename $rep .git)" && break
            done
        done
    fi
}

fetch(){
    echo "\$WEB_URL for web download"
    echo "\$GIT_URL for git clone or update"
    wget_by_array $WEB_URL
    git_clone_mirror_by_array $GIT_URL $TONE_BM_CACHE_DIR
}

install()
{
    echo "Default Install... Do nothing"
    install_pkg $DEP_PKG_LIST
}

setup()
{
    echo "Default Setup... Do nothing"
}

run()
{
    echo "Please implement run..."
}

teardown()
{
    echo "Default teardown... Do nothing"
}

parse()
{
    echo "Please implement parse..."
}

uninstall()
{
    echo "Default uninstall... Do nothing"
    uninstall_pkg
}

# print the command then execute it
# e.g.
#   logger echo "hello" ">" logfile
logger()
{
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
    eval "$@"
}

# print prompt information then quite
# usage:
#       die
#       die "Error blabla..."
die()
{
    if [ -n "$1" ]; then
        echo "$@" 1>&2
    else
        echo "Error occured and quite" 1>&2
    fi
    exit 1
}

# usage: bytes_convert NUM [KkMmGgTt]
bytes_convert()
{
    local b=$1
    shift
    local unit=$1
    case $unit in
        [kK]*) echo $((b >> 10))K;;
        [mM]*) echo $((b >> 20))M;;
        [gG]*) echo $((b >> 30))G;;
        [tT]*) echo $((b >> 40))T;;
            *) echo "Can not convert byte $b to $unit";;
    esac
}

# usage: size_cn_bytes [NUM][bBsSkKmMgGtT]
size_cn_bytes()
{
    local size=$1 num suffix

    num=${size%[bBsSkKmMgGtT][bB]}
    num=${num%[bBsSkKmMgGtT]}
    suffix=${size#$num}

    case $suffix in
        [bB]*) echo $num;;
        [kK]*) echo $(($num*1024));;
        [mM]*) echo $(($num*1024**2));;
        [gG]*) echo $(($num*1024**3));;
        [tT]*) echo $(($num*1024**4));;
            *) echo "nunowk suffix";;
    esac
}

# usage: get distro and kernel version
get_distro_kver()
{

    kver=$(uname -r | cut -d'.' -f1-2)

    if [ -f /etc/alinux-release ]; then

        if grep -q "release 2" /etc/alinux-release;then
            distro="alinux2"
        elif grep -q "release 3" /etc/alinux-release;then
            distro="alinux3"
        else
            distro="alinux"
        fi

    else
        distro=""
    fi
}

# preserve dirs or files: upload as results
upload_archives()
{
	[ -n "$1" ] || return
	[ -n "$TONE_CURRENT_RESULT_DIR" ] || return
	[ -d "$TONE_CURRENT_RESULT_DIR" ] || return
	local archive_path="$TONE_CURRENT_RESULT_DIR/archives"
	[ -d "$archive_path" ] || mkdir $archive_path
	cp -r $* $archive_path
}

# preserve dirs or files: upload as testlogs
upload_testlogs()
{
	[ -n "$1" ] || return
	[ -n "$TONE_CURRENT_RESULT_DIR" ] || return
	[ -d "$TONE_CURRENT_RESULT_DIR" ] || return
	local testlog_path="$TONE_CURRENT_RESULT_DIR/testlogs"
	[ -d "$testlog_path" ] || mkdir $testlog_path
	cp -r $* $testlog_path
}

# custom script
custom_script()
{
	[ -n "$TONE_CUSTOM_SCRIPT" ] || return
	if [[ "$TONE_CUSTOM_SCRIPT" =~ ^http.*:// ]]; then
		echo "wget $TONE_CUSTOM_SCRIPT -O $TONE_CURRENT_RESULT_DIR/custom_script"
		wget $TONE_CUSTOM_SCRIPT -O $TONE_CURRENT_RESULT_DIR/custom_script
	elif [ -f "$TONE_CUSTOM_SCRIPT" ]; then
		echo "cp $TONE_CUSTOM_SCRIPT $TONE_CURRENT_RESULT_DIR/custom_script"
		cp $TONE_CUSTOM_SCRIPT $TONE_CURRENT_RESULT_DIR/custom_script
	else
		echo "Invalid path of custom script: $TONE_CUSTOM_SCRIPT"
		return 1
	fi

	chmod +x $TONE_CURRENT_RESULT_DIR/custom_script
	logger $TONE_CURRENT_RESULT_DIR/custom_script
}

# retry given command N times
retry()
{
	local try_max=$1
	shift
	local try_cmd="$*"

	for i in `seq $try_max`; do
		echo "Try $i: $try_cmd"
		eval "$try_cmd" && break
		[ -n "$RETRY_DELAY" ] && sleep $RETRY_DELAY
	done
}
